package com.gxs.ea.examples;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

import org.jdom.Element;

public class ProcessTree {

	private ProcessTreeNode root = null;
	
	private String rootProcessId = null;
	
	private Map<String, ProcessTreeNode> mapOfProcessesAndEvents = new HashMap<String, ProcessTreeNode>();
	private Map<String, Integer> mapOfParentProcessIds = new HashMap<String, Integer>();

	public ProcessTreeNode getRoot() {
		return root;
	}

	public void setRoot(ProcessTreeNode root) {
		this.root = root;
	}
	
	public void addProcessEvent(String processId, String parentProcessId, Element event)
	{
		//
		// Record the process
		// Save the event
		if(mapOfProcessesAndEvents.get(processId) == null)
		{
			ProcessTreeNode node = new ProcessTreeNode();
			node.setProcessId(processId);
			node.setParentProcessId(parentProcessId);
			node.setEvents(new ArrayList<Element>());
			mapOfProcessesAndEvents.put(processId, node);
		}
		mapOfProcessesAndEvents.get(processId).getEvents().add(event);

		if(parentProcessId == null)
		{
			rootProcessId = processId;
		}
		else
		{
			//
			// Record that we have now seen this parent process id
			if(mapOfParentProcessIds.get(parentProcessId) == null)
			{
				mapOfParentProcessIds.put(parentProcessId, new Integer(1));
			}
			else
			{
				Integer count = mapOfParentProcessIds.get(parentProcessId);
				mapOfParentProcessIds.put(parentProcessId, count+1);
			}
		}
	}
	
	public ProcessTreeNode buildTree()
	{
		if(validateTreeHasSingleParent() == null)
		{
			System.out.println("Process Tree is not valid");
			return null;
		}
		
		System.out.println("Tree is valid");
		
		// Now Bulid the Tree
		ProcessTreeNode rootNode = mapOfProcessesAndEvents.get(rootProcessId);
		if(rootNode == null)
		{
			// possible if it' sonly referenced as parent and no events were found
			rootNode = new ProcessTreeNode();
			rootNode.setProcessId(rootProcessId);
			rootNode.setParentProcessId(null);
			rootNode.setParentNode(null);
		}
		else
		{
			rootNode.setParentNode(null);
			rootNode.setParentProcessId(null);
			mapOfProcessesAndEvents.remove(rootProcessId);
		}
		
		//
		// Now find and add all its direct children
		
		System.out.println("\t\tStarting the processes for lineage links Complete. Outstanding " + mapOfProcessesAndEvents.keySet().size());
		
		while(mapOfProcessesAndEvents.keySet().size() > 0)
		{
			System.out.println("\t\t\tInspecting the processes for lineage links --- current oustanding number is: " + mapOfProcessesAndEvents.keySet().size());
			
			checkForChildrenProcessEvaluationsRecursively(rootNode);
		}
		System.out.println("\t\t\tInspecting the processes for lineage links Complete. Outstanding " + mapOfProcessesAndEvents.keySet().size());
		
		//
		// Check how many event we have now
		
		
		int events = getEventCountsFromNodeAndChildren(rootNode);
		
		System.out.println("Total number of events across all processes " + events);
		
		return rootNode;
	}
	
	public int getEventCountsFromNodeAndChildren(ProcessTreeNode n)
	{
		int count = 0;
		if(n.getEvents() != null)
			count += n.getEvents().size();
		
		for (ProcessTreeNode childN : n.getChildProcesses())
		{
			count = count + getEventCountsFromNodeAndChildren(childN);
		}
		
		return count;
	}
	
	private void checkForChildrenProcessEvaluationsRecursively(
			ProcessTreeNode rootNode) 
	{
		attachChildrentToSpecifiedNode(rootNode);
		
		for(ProcessTreeNode node :  rootNode.getChildProcesses())
		{
			checkForChildrenProcessEvaluationsRecursively(node);
		}
		
	}

	private void attachChildrentToSpecifiedNode(ProcessTreeNode startingNode) 
	{
		List<ProcessTreeNode> nodesToDetach = new ArrayList<ProcessTreeNode>();
		
		for(ProcessTreeNode currentChild : mapOfProcessesAndEvents.values() )
		{
			if(currentChild.getParentProcessId().equals(startingNode.getProcessId()))
			{
				nodesToDetach.add(currentChild);
			}
		}
		
		for(ProcessTreeNode currentChNode : nodesToDetach)
		{
			if(startingNode.getChildProcesses() == null)
			{
				startingNode.setChildProcesses(new ArrayList<ProcessTreeNode>());
			}
			
			startingNode.getChildProcesses().add(currentChNode);
			currentChNode.setParentNode(startingNode);
			mapOfProcessesAndEvents.remove(currentChNode.getProcessId());
		}
		
	}

	public String validateTreeHasSingleParent()
	{
		
		for(String parentId : mapOfParentProcessIds.keySet() )
		{
			if(mapOfProcessesAndEvents.get(parentId) == null)
			{
				if(rootProcessId == null)
					rootProcessId = parentId;
				else if(rootProcessId.equals(parentId))
				{
					// it's okay/ maybe we ran the method twice. 
				}
				else
				{
					System.out.println("More than 1 root found so far: " + rootProcessId + ", and " + parentId);
					return null;
				}
			}
		}
		
		return rootProcessId;
	}
}
